/*
 * File     : CustomNativeObject.java
 * Created  : 11 Dec 2011
 *
 * Copyright © 2011 Matthew Wilson (mj. {my-surname} .uk {at} gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.dni.type;

import com.googlecode.dni.library.Library;
import com.googlecode.dni.type.structure.Structure;
import com.googlecode.dni.type.structure.StructureOptions;

/**
 * <p>
 *  Provides a base class for any user-defined types that are backed by native
 *  memory.
 * </p>
 * <p>
 *  This type is distinct from {@link Structure} (even when
 *  {@linkplain StructureOptions#opaque() opaque}) in that:
 * </p>
 * <ul>
 *  <li>a concrete can be used;</li>
 *  <li>user-defined methods can be added;</li>
 *  <li>class hierarchies modelling native pseudo-hierarchies can be used.</li>
 * </ul>
 * <p>
 *  No additional permanent state must be stored in the Java object.  The object
 *  must only be a lightweight wrapper.  Instances are not cached by DNI;
 *  therefore, it must be possible to create instances with just a pointer.
 * </p>
 * <p>
 *  The type must define at least one of:
 * </p>
 * <ul>
 *  <li>
 *   a constructor:
 *   <code>public <i>MyType</i>( {@link Pointer} pointer )</code>
 *  </li>
 *  <li>
 *   a factory method:
 *   <code>public static <i>MyType</i> create( {@link Pointer} pointer )</code>
 *  </li>
 * </ul>
 * <p>
 *  If both are present, the factory method is used by DNI.  The constructor
 *  and/or factory methods must be defined on the base type (as declared on the
 *  {@link Structure} or {@link Library} interface that uses it).  However, the
 *  factory method may validly create an instance of a sub-type.
 * </p>
 * <p>
 *  No instance may wrap the NULL (zero) pointer.
 * </p>
 * <p>
 *  Types may refine the behaviour of {@link #equals(Object)} and
 *  {@link #isSameObject(NativeObject)} according to the behaviour of the type
 *  hierarchy.  However, remember that both methods must be symmetric.
 *
 *
 * @author Matthew Wilson
 */
public abstract class CustomNativeObject implements NativeObject
{

    private final long address;


    /**
     * Creates a new instance.
     *
     * @param pointer
     *            the pointer
     */
    protected CustomNativeObject( final Pointer pointer )
    {
        this.address = pointer.address();
        if ( this.address == 0L )
        {
            throw new NullPointerException( "Address was NULL" );
        }
    }

    /** {@inheritDoc} */
    @Override
    public final Pointer pointer()
    {
        return Pointer.fromAddress( this.address );
    }

    /** {@inheritDoc} */
    @Override
    public final boolean isSameObject( final NativeObject other )
    {
        if ( !( other instanceof CustomNativeObject ) || !isSameObjectType( (CustomNativeObject) other ) )
        {
            return false;
        }
        CustomNativeObject otherObject = (CustomNativeObject) other;
        return otherObject.address == this.address;
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals( final Object obj )
    {
        if ( !( obj instanceof CustomNativeObject ) || !isSameObjectType( (CustomNativeObject) obj ) )
        {
            return false;
        }
        CustomNativeObject otherObject = (CustomNativeObject) obj;
        return otherObject.address == this.address;
    }

    /** {@inheritDoc} */
    @Override
    public final int hashCode()
    {
        return (int) ( this.address >>> 32L ) ^ (int) this.address;
    }

    /** {@inheritDoc} */
    @Override
    public String toString()
    {
        return String.format( "%s[0x%016x]", getClass().getSimpleName(), this.address );
    }

    /**
     * Obtains the address of the given object.  This method is for internal use
     * by DNI.
     *
     * @param object
     *            the object
     * @return the address
     */
    public static long getAddress( final CustomNativeObject object )
    {
        return object == null ? 0L : object.address;
    }

    /**
     * <p>
     *  Determines if the object is of the same type as this object.
     * </p>
     * <p>
     *  This method is used by {@link #isSameObject(NativeObject)} and
     *  {@link #equals(Object)}.  The default implementation only considers
     *  exactly the same class as being the same type.
     * </p>
     *
     * @param other
     *            the other object (not <code>null</code>)
     * @return <code>true</code> if the other object is of the same type
     */
    protected boolean isSameObjectType( final CustomNativeObject other )
    {
        return other.getClass() == getClass();
    }

}
